package kayao.server.network.message;

import java.util.Date;
import java.util.Vector;

import kayao.monitor.ServerMonitor;
import kayao.server.DesktopServer;
import kayao.server.network.NetreadThread;
import kayao.server.network.NetwriteThread;
import kayao.common.KayaoValues;



public class MessageHandler {
	//The queues of messages, each index express the priority.
	protected static Vector<IncomingMessage> mIncoming;
	protected static Object mIncomingLock=new Object();
	protected static Vector<Message> mOutgoing;
	protected static Object mOutgoingLock=new Object();
	protected static Vector<Message> mResponse;
	protected static Object mResponseLock=new Object();
	protected static MessageThread mMessageThread=null;
	protected static NetwriteThread mNetwriteThread = null;
	protected static NetreadThread mNetreadThread = null;
	protected static int []mMsgStats = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	protected static Date mStartTime;
	
	public static void initialize(NetwriteThread netwriteThread, NetreadThread netreadThread,
			MessageThread messageThread) {
		mIncoming=new Vector<IncomingMessage>();
		mOutgoing=new Vector<Message>();
		mResponse=new Vector<Message>();
		mMessageThread=messageThread;
		mNetwriteThread = netwriteThread;
		mNetreadThread = netreadThread;
		mStartTime = new Date();
	}
	
	public static void start() {
		mMessageThread.start();
	}
	
	public static boolean terminate(){
		while(mMessageThread.terminate())
		mIncoming = null;
		mOutgoing = null;
		mResponse = null;
		return false;
	}
	
	public static void printStats(){
		System.out.println("============[ KAYAO STATISTICS ]=========================");
		System.out.println("PLAYING_REQUEST_MESSAGE: "+mMsgStats[KayaoValues.PLAYING_REQUEST_MESSAGE]);
		System.out.println("PLAYING_REQUEST_REPLY_MESSAGE: "+mMsgStats[KayaoValues.PLAYING_REQUEST_REPLY_MESSAGE]);
		System.out.println("KING_REQUEST_MESSAGE: "+mMsgStats[KayaoValues.KING_REQUEST_MESSAGE]);
		System.out.println("KING_REQUEST_REPLY_MESSAGE: "+mMsgStats[KayaoValues.KING_REQUEST_REPLY_MESSAGE]);
		System.out.println("SUSCRIPTION_REQUEST: "+mMsgStats[KayaoValues.SUSCRIPTION_REQUEST]);
		System.out.println("SUSCRIPTION_REQUEST_ANSWER: "+mMsgStats[KayaoValues.SUSCRIPTION_REQUEST_ANSWER]);
		System.out.println("SUSCRIPTION_REQUEST_NEGATIVE_ANSWER: "+mMsgStats[KayaoValues.SUSCRIPTION_REQUEST_NEGATIVE_ANSWER]);
		System.out.println("NEW_MATE_ARRIVED: "+mMsgStats[KayaoValues.NEW_MATE_ARRIVED]);
		System.out.println("STATE_UPDATE: "+mMsgStats[KayaoValues.STATE_UPDATE]);
		System.out.println("NEW_MATE_REPLY: "+mMsgStats[KayaoValues.NEW_MATE_REPLY]);
		System.out.println("LOGOUT_MSG: "+mMsgStats[KayaoValues.LOGOUT_MSG]);
		System.out.println("TEAM_COMMUNICATION: "+mMsgStats[KayaoValues.TEAM_COMMUNICATION]);
		System.out.println("TEAM_COMMUNICATION_REPLY: "+mMsgStats[KayaoValues.TEAM_COMMUNICATION_REPLY]);		
		System.out.println("WHAT_TIME_IS_IT_MESSAGE: "+mMsgStats[KayaoValues.WHAT_TIME_IS_IT_MESSAGE]);
		System.out.println("TIME_IS_MESSAGE: "+mMsgStats[KayaoValues.TIME_IS_MESSAGE]);
		System.out.println("DEAD_CLIENT: "+mMsgStats[KayaoValues.DEAD_CLIENT]);
		System.out.println("NEW_MISSION: "+mMsgStats[KayaoValues.NEW_MISSION]);
		System.out.println("UNSUBSCRIBE_MSG: "+mMsgStats[KayaoValues.UNSUBSCRIBE_MSG]);
		System.out.println("FLAG_COLLISION: "+mMsgStats[KayaoValues.FLAG_COLLISION]);
		System.out.println("");
		System.out.println("Total Runtime: "+convertUptime((System.currentTimeMillis()-mStartTime.getTime())));
		System.out.println("Total number of packets: "+mMsgStats[0]);		
		System.out.println("=========================================================");
	}
	
	public static void statistics(int msg, String source){
		mMsgStats[0] = (mMsgStats[0]+1);
		switch (msg) {
			case KayaoValues.PLAYING_REQUEST_MESSAGE: 				mMsgStats[KayaoValues.PLAYING_REQUEST_MESSAGE]++; break;
			case KayaoValues.PLAYING_REQUEST_REPLY_MESSAGE: 		mMsgStats[KayaoValues.PLAYING_REQUEST_REPLY_MESSAGE]++; break;
			case KayaoValues.KING_REQUEST_MESSAGE: 					mMsgStats[KayaoValues.KING_REQUEST_MESSAGE] = (mMsgStats[KayaoValues.KING_REQUEST_MESSAGE]+1); break;
			case KayaoValues.KING_REQUEST_REPLY_MESSAGE: 			mMsgStats[KayaoValues.KING_REQUEST_REPLY_MESSAGE]++; break;
			case KayaoValues.SUSCRIPTION_REQUEST: 					mMsgStats[KayaoValues.SUSCRIPTION_REQUEST]++; break;
			case KayaoValues.SUSCRIPTION_REQUEST_ANSWER: 			mMsgStats[KayaoValues.SUSCRIPTION_REQUEST_ANSWER]++; break;
			case KayaoValues.SUSCRIPTION_REQUEST_NEGATIVE_ANSWER: 	mMsgStats[KayaoValues.SUSCRIPTION_REQUEST_NEGATIVE_ANSWER]++; break;
			case KayaoValues.NEW_MATE_ARRIVED: 						mMsgStats[KayaoValues.NEW_MATE_ARRIVED]++; break;
			case KayaoValues.STATE_UPDATE: 							mMsgStats[KayaoValues.STATE_UPDATE]++; break;
			case KayaoValues.NEW_MATE_REPLY: 						mMsgStats[KayaoValues.NEW_MATE_REPLY]++; break;
			case KayaoValues.LOGOUT_MSG: 							mMsgStats[KayaoValues.LOGOUT_MSG]++; break;
			case KayaoValues.TEAM_COMMUNICATION: 					mMsgStats[KayaoValues.TEAM_COMMUNICATION]++; break;
			case KayaoValues.WHAT_TIME_IS_IT_MESSAGE: 				mMsgStats[KayaoValues.WHAT_TIME_IS_IT_MESSAGE]++; break;
			case KayaoValues.TIME_IS_MESSAGE: 						mMsgStats[KayaoValues.TIME_IS_MESSAGE]++; break;
			case KayaoValues.DEAD_CLIENT: 							mMsgStats[KayaoValues.DEAD_CLIENT]++; break;
			case KayaoValues.TEAM_COMMUNICATION_REPLY: 				mMsgStats[KayaoValues.TEAM_COMMUNICATION_REPLY]++; break;
			case KayaoValues.NEW_MISSION:			 				mMsgStats[KayaoValues.NEW_MISSION]++; break;
			case KayaoValues.UNSUBSCRIBE_MSG: 						mMsgStats[KayaoValues.UNSUBSCRIBE_MSG]++; break;
			case KayaoValues.FLAG_COLLISION: 						mMsgStats[KayaoValues.FLAG_COLLISION]++; break;
			
			default: 												System.out.println("MessageHandler.statistics: This should never happen, msgtype: "+msg+" source: "+source); break;
		}
	}	
	
	
	/**
	 * It gets the next Message from the correspondent queue.
	 * @param priority The priority of the queue.
	 * @return The message. Null if that queue is empty
	 */
	public static Message getMessageOutgoing() {
		synchronized(mOutgoingLock){
			if(mOutgoing.size()==0)
					return null;
			else
			{
					Message m=mOutgoing.get(0);
					mOutgoing.remove(0);
					return m;
			}
		}
	}

	public static IncomingMessage getMessageIncoming() {
		synchronized(mIncomingLock){
				if(mIncoming.size()==0)
				{
					return null;
				}
				else
				{
					IncomingMessage m=mIncoming.get(0);
					mIncoming.remove(0);
					return m;
				}
			}
	}

	/**
	 * It gets the next message in the waiting queue and delete it
	 */
	public static Message getNextMessageAwaitingReply() {
		synchronized(mResponseLock){
		if(mResponse.size()==0)
			return null;
		else
		{
			Message m=mResponse.get(0);
			return m;
		}
		}
	}
	public static boolean gotMessagesAwaitingReply() {
		synchronized(mResponseLock){
		if(mResponse.size()!=0)
			return true;
		return false;
		}
	}
	
	public static boolean gotMessagesToSend() {
		synchronized(mOutgoingLock){
		if(mOutgoing.size()!=0)
			return true;
		return false;
		}
	}

	public static boolean gotMessagesToRead() {
		synchronized(mIncomingLock){
		if(mIncoming.size()!=0)
			return true;
		return false;
		}
	}

	/**
	 * @return False if the write thread can go to sleep
	 * 		   True if there are messages waiting for reply or to be sent
	 */
	public static boolean gotMessagesInQueue() {
		synchronized(mOutgoingLock){
		if(mOutgoing.size()!=0)
			return true;
		}
		synchronized(mResponseLock){
		if(mResponse.size()!=0)
			return true;
		return false;
		}
	}

	public static void insertIncomingMessage( String ip, byte[] data) 
	{
		synchronized(mIncomingLock){
			mIncoming.add(new IncomingMessage(ip,data,0));
			synchronized(mMessageThread) {
				mMessageThread.notify();
			}
		}
	}
	
	public static void insertOutgoingMessage(Message outgoingMessage) {
		synchronized(mOutgoingLock){
			mOutgoing.add(outgoingMessage);
			synchronized(NetwriteThread.LOCK) {
				NetwriteThread.LOCK.notify();
			}
			//System.out.println("Making an inerrupt if sleeping from insertOutgoingMessage");
			mNetwriteThread.interrupt();
		}
	}
	
	/**
	 * It makes a sorted insertion in the awaiting response list by time
	 * @param m the message to insert
	 * @param ip 
	 */
	public static void insertAwaitingResponseMessage(Message m) {
		synchronized(mResponseLock){
			for(int i=0;i<mResponse.size();i++)
			{
				if(mResponse.get(i).getClass().equals(m.getClass()) && 
						m.getTargetAddress().equals(mResponse.get(i).getTargetAddress()))
				{
					if(m.getRetriesLeft()<mResponse.get(i).getRetriesLeft())return;
					m.setTimeout(mResponse.get(i).getTimeout());
					mResponse.set(i, m); //timeout??
					return;
				}
			}
			for(int i=0;i<mResponse.size();i++)
			{
				if(mResponse.get(i).mTimeout>m.getTimeout())
				{
					mResponse.insertElementAt(m, i);
					return;
				}
			}
			mResponse.add(m);
			//System.out.println("Making an inerrupt if sleeping from insertAwaitingResponseMessage");
			mNetwriteThread.interrupt();
		}
	}
	
	public static void removeAwatingMessage(int c) 
	{
		synchronized(mResponseLock){	
			for(int i=0;i<MessageHandler.mResponse.size();i++)
			{
				if(MessageHandler.mResponse.get(i).mMessageId==c)
				{
					MessageHandler.mResponse.remove(i);
					System.out.println("[MessageHandler] - MSG with int: "+ c+" deleted from queue of awaiting.");
					return;
				}
			}
		}
	}
	/**
	 * An answer has come, we have to delete it from the mResponse queue
	 * @param m: the message
	 */
	public static void removeAwatingMessage(IncomingMessage m) 
	{
		synchronized(mResponseLock){
			for(int i=0;i<MessageHandler.mResponse.size();i++)
			{
				if(MessageHandler.mResponse.get(i).mMessageId==m.getCounter())
				{
					MessageHandler.mResponse.remove(i);
					System.out.println("[MessageHandler] - MSG with id: "+ m.getCounter()+" deleted from queue of awaiting.");
					return;
				}
			}
			//System.out.println("[MessageHandler] - MSG with id: "+ m.getCounter()+" COULDN'T be deleted from queue of awaiting.");
		}
	}

	/**
	 * Get awaiting message from the mResponse queue
	 * and delete it from the mResponse
	 * @param k: message id of packet
	 */
	public static Message getAwaitingMessage(int k)
	{
		synchronized(mResponseLock){

			for(int i=0;i<MessageHandler.mResponse.size();i++)
			{
				//System.out.println("MessageHandler.getAwaitingMessage: if "+MessageHandler.mResponse.get(i).mMessageId+"=="+k);
				if(MessageHandler.mResponse.get(i).mMessageId==k)
				{
					Message msg = MessageHandler.mResponse.get(i);
					MessageHandler.mResponse.remove(i);
					return msg;
				}
			}
			return null;
		}
	}

	public static void setLogoutCountInNetwriteThread(int x){
		mNetwriteThread.setLogoutCount(x);
	}
	
	public static void decreaseLogoutCounter(){
		mNetwriteThread.decreaseLogoutCounter();
	}
	
    public static String convertUptime(long uptime) {
		String format = String.format("%%0%dd", 2);
		uptime = uptime / 1000;
		String seconds = String.format(format, uptime % 60);
		String minutes = String.format(format, (uptime % 3600) / 60);
		String hours = String.format(format, uptime / 3600);
		String time =  hours + ":" + minutes + ":" + seconds;
    	return time;  
    }

}
